/*	Rais_Utils: 一些乱七八糟的工具函数, Just a toy.
	啊, 更多的是一堆shit.

	Created: 2020.4.19
	by Raiscies.
	
	modified: 2020.5.31 - removed a dozen of shit (chicken ribs!) functions and classes.
	
	为了节省内存空间, 所有的函数或类在不使用时都不会被链接.
	
	
	Be a geeker person!	Keep a hot heart for technology! 			——2020.11.7
*/

#ifndef RAIS_UTILS_H
#define RAIS_UTILS_H

#include <HardwareSerial.h>	//for serial printing
#include <math.h>


//声明一个非模板函数只在被调用时链接
#define	lazy_link  template <typename = void
//声明一个非模板类只在有被使用时链接
#define lazy_class(cls_name)                        \
template <typename = void> class Lazy_##cls_name;   \
using cls_name = Lazy_##cls_name<>;                 \
template <typename>                                 \
class Lazy_##cls_name

//获取以上面方式创建的类的名字 - 用于构造函数
#define lz(cls_name) Lazy_##cls_name

//eg: lazy_link> void func(){...}
//eg: lazy_class(Cls){ ... };


//使一个未被使用的变量在编译时不发出警告
#ifndef unused
#define unused(var)	static_cast<void>(var)
#endif


extern void randomSeed(unsigned long);	//in Arduino.h
using byte = unsigned char;
using num_t = unsigned int;	//data num type	


//32x8及32x32 开关量二维数组:
using line32_t	= byte[4];
using mat32x8_t	= byte[4][8];
using mat32s_t	= byte[4][32];
//operates:
template <typename M>
inline bool mget(M& mat, byte x, byte y){return (mat[x >> 3][y] >> (7 - (x & 7))) & 1;}
template <typename M>
inline void mset(M& mat, byte x, byte y, bool state){
	state ? mat[x >> 3][y] |=  (128 >> (x & 7))		//set true
		  : mat[x >> 3][y] &= ~(128 >> (x & 7));	//set false
}
inline bool mget(line32_t& line, byte x){return (line[x >> 3] >> (7 - (x & 7))) & 1;}
inline void mset(line32_t& line, byte x, bool state){
	state ? line[x >> 3] |=  (128 >> (x & 7))		//set true
		  : line[x >> 3] &= ~(128 >> (x & 7));	//set false
}

//该方法将在一个断电保存记忆体(如EEPROM)中地址为 addr_1 和 addr_2 的位置存储一个类型为 unsigned int 的数, 作为其随机数种子.
//这个记忆体需要实现read(addr) 和write(addr, val) 方法.
//这个数每次调用将加一. 可将其放在setup域中.
//eg: 在引入EEPROM.h 的情况下, 调用: betterRandomSeed(EEPROM);
template <class M>
unsigned int betterRandomSeed(M& memory, num_t addr_1 = 0x0, num_t addr_2 = 0x1){
	byte high = memory.read(addr_1), low = memory.read(addr_2);
	unsigned int count = ((high << 8) | low) + 1;
	if(high != static_cast<byte>(count >> 8))	memory.write(addr_1, static_cast<byte>(count >> 8));
	if(low  != static_cast<byte>(count & 0xff))	memory.write(addr_2, static_cast<byte>(count & 0xff));
	randomSeed(count);
	return count;
}


//for arduino envirnment only:
//easiler printing method:

lazy_link> inline void sb(unsigned long baudrate = 9600){
	Serial.begin(baudrate);
	while(not Serial);
}

#define edl	'\n'		//line feed
lazy_link> void sp(){}
lazy_link> void spl(){Serial.println();}
template <typename T, typename... Ts>
void sp(T arg, Ts... args){
	Serial.print(arg);
	return sp(args...);
}
template <typename T> void spl(T arg){Serial.println(arg);}
template <typename T, typename... Ts>
void spl(T arg, Ts... args){
	Serial.println(arg);
	return spl(args...);
}
//eg: sp("this is a Test, the value is:", variable, ", value 2 is:" , var2, edl, "this is the last string");
//eg: spl('H', 'A' ,edl, 'H', 'A' , edl, 'H', 'A' );

//tools for debugging:
#ifndef NO_SERIAL_DEBUG

#define look(expr_or_var...) 								do{ sp(F(")  "#expr_or_var": "), expr_or_var); spl(); }while(0)

#define ASSERT_FAILED_DELAY 500

#define _asserting(delayTime, line, expr...)				do{ if(not (expr)){ spl(F(")! Assert failure: expression \""#expr"\" is false in line "#line".")); delay(delayTime);}	}while(0)
#define _assert_do(line, do_sth, expr...)					do{ if(not (expr)){ spl(F(")! Assert failure: expression \""#expr"\" is false in line "#line".")); do_sth; }			}while(0)
#define _assert_var(delayTime, line, var, expr...)			do{ if(not (var expr)){ sp(F(")! Assert failure: expression \""#var" "#expr"\" is false in line "#line", when "#var" = "), var, '.', edl); delay(delayTime);} }while(0)

#define assert_helper(delayTime, line, expr...)				_asserting(delayTime, line, expr)
#define assert_do_helper(line, expr...)						_assert_do(line, expr)
#define assert_var_helper(delayTime, line, var, expr...)	_assert_var(delayTime, line, var, expr)

#define asserting(expr...)						asserting_helper(ASSERT_FAILED_DELAY, __LINE__, expr)
#define assert_do(do_sth, expr...)				assert_do_helper(__LINE__, do_sth, expr)
#define assert_var(var, expr...)				assert_var_helper(ASSERT_FAILED_DELAY, __LINE__, var, expr)

//eg: look(16 < 3);						print:	")  16 < 3: false"
//eg: asserting(2 > 3)					print:	")! Assert failure: expression "2 > 3" is false in line 110."				and do: delay(1000);
//eg: assert_do(while(1), 2 > 3)		print:	")! Assert failure: expression "2 > 3" is false in line 111."				and do: while(1);
//eg: assert_var(i, < 3)				print:	")! Assert failure: expression "i < 3" is false in line 112, when i = 4"	and do: delay(1000);

#else
#define look(expr_or_var...) 			do{}while(0)
#define asserting(expr...)				do{}while(0)
#define assert_do(do_sth, expr...)		do{}while(0)
#define assert_var(var, expr...)		do{}while(0)

#endif

//macro debugging:
#define R_TO_STRING_helper(args...) #args
#define R_TO_STRING(args...) R_TO_STRING_helper(args)
#define macro_result(codes_with_macro...) static_assert(0, R_TO_STRING(codes_with_macro))
#define no_macro_result(codes_with_macro...) //easily remove test block
/*	'macro_result' is used to replace all macros in the codes and print as errors.
	eg:
	
	macro_result(
		forange(i, 0, 100, 5){
			do_something(); 
		}
		
		__LINE__ ;
		__FILE__ ;
		__DATE__ ;
		__TIME__ ;
		
	);
	
*/


//unsigned binary literal define:
//for NOT NEGATIVE number ONLY!
template <class T = unsigned long long>
constexpr T bin(unsigned long long dec){
	return (dec & 1) | ((dec / 10) != 0 ? (bin(dec / 10) << 1) : 0);
}
//the second parameter just for recursion.
template <class T = unsigned long long> 
constexpr T bin(char const* dec, unsigned long long tmp = 0){
	//The greatest truths are the simplest.
	return (*dec != '\0' ? bin(dec + 1, (tmp << 1) | (*dec == '0' ? 0 : 1) ) : tmp);
}
constexpr unsigned long long operator"" _b(char const* dec){
	return bin(dec);
	//eg: 1011101111_b  ->  751
}
/*	last function wasn't been called when a number out of bounds of type unsigned long long,
	so I have to remove this function:
	constexpr unsigned long long operator"" _b(unsigned long long dec){
		return bin(dec);
	}
*/


//for get the type that removed its reference or pointer decoration:
template <class T> struct TypeExtractor     {using type = T;};
template <class T> struct TypeExtractor<T&> {using type = T;};
template <class T> struct TypeExtractor<T*> {using type = T;};
#define extype(obj_or_expr...)  TypeExtractor<decltype(obj_or_expr)>::type
#define T_extype(T)             TypeExtractor<T>::type
//TypeExtractor<decltype(pointer)>::type 			->return pointer's original type(such as int* -> int).
//sample of macro:  extype(*this) or extype(this)	->return the instance's type , not its reference or pointer type!


template <typename T, typename... Ts>
static inline T  varargs_firstArg(T& t, Ts... ts){return t;}
template <typename T, typename... Ts>
static inline T* varargs_firstArgPtr(T& t, Ts... ts){return &t;}
//null argument:
static inline void* varargs_firstArg(){return nullptr;}
static inline void* varargs_firstArgPtr(){return nullptr;}

template <typename LT>
static inline void list_putVarargs(LT* list, unsigned long idx){ unused(list); unused(idx); }
template <typename LT, typename T, typename... Ts>
static void list_putVarargs(LT* list, unsigned long idx, T arg, Ts... args){
	list[idx] = static_cast<LT>(arg);
	list_putVarargs(list, idx + 1, args...);
}


template <typename... Ts>
auto newlist(Ts... args)->decltype(varargs_firstArgPtr(args...)) {
	decltype(varargs_firstArgPtr(args...)) list = nullptr;
	if(sizeof...(args) != 0){
		list = new decltype(varargs_firstArg(args...)) [sizeof...(args)];
		list_putVarargs(list, 0, args...);	
	}
	return list;
}
template <typename T, typename... Ts>
T* newlist(Ts... args){
	T* list = nullptr;
	if(sizeof...(args) != 0) {
		list = new T[sizeof...(args)];
		list_putVarargs(list, 0, args...);
	}
	return list;
}


//for range looping:
//tools:
template <class T_FROM, class T_TO>
inline bool forange_steppingDirection(long& var, T_FROM from, T_TO to){
	return from < to ? var < to : var > to;
}
template <class T_FROM, class T_TO, class T_STEP>
inline void forange_stepping(long& var, T_FROM from, T_TO to, T_STEP step){
	from < to ? var += step : var -= step;
}
// #define forange_steppingDirection(var_name, from, to)		(from) < (to) ? var_name < (to)	: var_name > (from)
// #define forange_stepping(var_name, from, to, step)		(from) < (to) ? var_name += (step)	: var_name -= (step)

#define range_4_args(var_name, from, to, step)	(long var_name = (from); forange_steppingDirection(var_name, from, to); forange_stepping(var_name, from, to, step))
#define range_3_args(var_name, from, to)		range_4_args(var_name, from, to, 1)
#define range_2_args(var_name, times)			(unsigned long var_name = 0; var_name < (times); var_name ++)
#define range_1_arg(times, line)				(unsigned long itr_##line = 0; itr_##line < (times); itr_##line ++)

#define range_1_arg_helper1(times, line)		range_1_arg(times, line)
#define range_1_arg_helper2(times)				range_1_arg_helper1(times, __LINE__)

#define range_switch(_1, _2, _3, _4, func_name, args...) func_name
//final target function:
#define forange(args...) for range_switch(args, range_4_args, range_3_args, range_2_args, range_1_arg_helper2)(args)
/*	eg:
	forange(32) 			Serial.println("hello!");							//print 32 times "hello!"
	forange(i, 10){			Serial.print(i); Serial.println(". hello!"); } 		//print 10 times "hello!" with index, like: 9. hello!
	forange(j, 90, -44) 	Serial.println(j);									//print numbers from 90 to -44
	forange(k, -40, 100, 3) Serial.println(k);									//print numbers from -40 to 100 by each 3 steps
*/

#define wait_until(condition) do{}while(not (condition))

//switch expression:
#define switch_exp(val, cases...)  \
[&]{                               \
    switch(val){                   \
        cases                      \
    }                              \
}() 

/*	
	'switch expression' eg:
	var = switch_exp(i, 
		case 1: return i;
		case 2: return i++;
		case 3: return i * 2;
		default:return i * i;
	);
*/

#define timing(tmstamp_f, codes...)   \
[&]{                                  \
    auto timestamp = tmstamp_f();     \
    {codes}                           \
    return tmstamp_f() - timestamp;   \
}()
/*  timing eg:
	spl( 
		timing(millis, 
			forange(i, 32) static_cast<double>(i) / 7;
		)
	);
	
*/

//Anonymous classes:
#define anocls(name_and_bases...)            \
[&]{                                         \
    class name_and_bases{                    \
        anocls_statements            

#define anocls_statements(statements...)     \
        statements                           \
    }obj;                                    \
    return obj;                              \
}()

/*  Anonymous class eg:
	template<typename AC_T> void testfunc(AC_T&& obj){ 
		//a testing fucntion to receive inner class's object.
	}
	
	testfunc(
		anocls(My_AnonymousClass: public Base1, Base2)(
			void interface1(){
				//do something...
			}
			void interface2(){
				//do something...
			}
		)
	);
*/


template <class T = long>
constexpr inline unsigned long ringlize(T idx, unsigned long length){
	return (idx < 0 ? -(idx + 1) : idx) % length;
}

class Ring {
	public:
	unsigned long length;
	Ring(unsigned long len = 1): length(len){}
	
	template <class T = long>
	unsigned long operator()(T idx){
		return (idx < 0 ? -(idx + 1) : idx) % length;
	}
	template <class T = long>
	unsigned long ringing(T idx){
		return idx == 0 ? 0 : this->operator()<T>(idx);
	}
};


//inline template functions
template <class T>
inline constexpr T& sf_abs(T& a){
	a = a < 0 ? -a : a;
	return a;
}

template <class T, class T2, class T3>
inline constexpr T& sf_constrain(T& x, T2&& downner, T3&& upper){
	x = x < downner ? downner : (x > upper ? upper : x);
	return x;
}

//勿删, 有依赖.
template <class T, class T2, class T3>
inline constexpr auto r_constrain(T&& x, T2&& downner, T3&& upper)->decltype(x < downner ? downner : (x > upper ? upper : x)) {
	 return x < downner ? downner : (x > upper ? upper : x);
}

template <class T, class RT, class RT2>
inline constexpr bool inRange(T&& x, RT&& downer, RT2&& upper){
	return x >= downer && x <= upper;
}

//linear mapping
template <class T, class IT, class OT>
inline constexpr T linearMap(T&& x, IT&& minIn, IT&& maxIn, OT&& minOut, OT&& maxOut){
	return maxIn == minIn ? (maxOut - minOut) / 2 : ((x - minIn) * (maxOut - minOut) / (maxIn - minIn) + minOut);
}


template <class T>
inline void swap(T& a, T& b){
	T tmp = a;
	a = b;
	b = tmp;
}

//for integer variables swap
template <class T>
inline void swap_i(T& a, T& b){
	// a ^= b;
	// b ^= a;
	// a ^= b;
	a ^= b ^= a ^= b;
}


//solving Quadratic Equation: ax^2 + bx + c = 0
lazy_link> void solveQuadEqua(double solution[], double a, double b, double c){
	if(solution == nullptr) return;
	double delta = b * b - 4 * a * c;
	if(delta < 0) {
		solution[0] = NAN;
		solution[1] = NAN;
	}else {
		solution[0] = -(b + sqrt(delta)) / (2 * a);
		solution[1] = -(b - sqrt(delta)) / (2 * a);
	}
}


/*	classes:
	没卵用, 因此移除.
	

//向量类:
lazy_link> class Vector{
	public:
	double x = 0, y = 0;
	
	Vector(double x, double y);
	
	Vector(const Vector& v);
	
	Vector();
	
	void set(double x, double y);
	
	void operator=(const Vector& v);
	
	//向量加减
	Vector operator+(const Vector& v);
	Vector operator-(const Vector& v);
	void operator+= (const Vector& v);
	void operator-= (const Vector& v);
	
	//数乘
	Vector operator*(double l);
	void operator*= (double l);
	
	//数量积
	double operator*(const Vector& v);
	
	//相等向量判断
	bool operator==(const Vector& v);
	
	//模大小比较
	bool operator> (const Vector& v);
	bool operator< (const Vector& v);
	bool operator>=(const Vector& v);
	bool operator<=(const Vector& v);
	
	//向量翻转
	Vector operator~();	
};


// 数据处理与分析类:
// 应使用有符号整型或浮点型(不想兼容无符号型了...)

template <class T> 
class DataAnalysis{
	
	T* data = nullptr;
	num_t dataNum;
	num_t pos = 0;
	
	//最大值	最小值	
	T maxData, minData;
	//平均值	方差	标准差
	T average, variance, std_deviation;
	
	public:
	DataAnalysis(T defaultValue, num_t dataNum = 32){
		if(dataNum <= 1) dataNum = 2;	//the smallist dataNum is 2.
		this->dataNum = dataNum;
		data = new T[dataNum];
		for(num_t i = 0; i < dataNum; i++){
			data[i] = defaultValue;
		}
	}
	DataAnalysis(num_t dataNum = 32){
		if(dataNum <= 1) dataNum = 2;	//the smallist dataNum is 2.
		this->dataNum = dataNum;
		data = new T[dataNum];
	}
	~DataAnalysis(){
		delete data;
	}
	
	template <class URT> 
	void push(URT&& newData, bool calc = true){
		if(pos >= dataNum) pos = 0;
		data[pos] = newData;
		calc && calcAll();	//if(calc) calcAll();
	}
	
	private:
	//data handle functions
	
	//寻找最大值和最小值
	void calcMax_Min(){
		T mx = data[0], mn = mx;
		for(num_t i = 1; i < dataNum; i++){
			(mx < data[i]) && mx = data[i];
			(mn > data[i]) && mn = data[i];
		}
		maxData = mx;
		minData = mn;
	}

	
	//取平均数
	void calcAverage(){
		T tmp = 0;
		for(num_t i = 0; i < dataNum; i++){
			tmp += data[i];
		}
		average = tmp / dataNum;
	}
	
	//计算方差 - 使用了平均数
	void calcVariance(){
		T tmp = 0;
		for(num_t i = 0; i < dataNum; i++){
			tmp += r_sq(data[i] - average);
		}
		variance = tmp / dataNum;
	}
	
	//计算标准差 - 使用了方差
	void calcStd_deviation(){
		std_deviation = sqrt(variance);
	}
	
	public:
	//计算所有数据
	void calcAll(){
		calcMax_Min();
		calcAverage();
		calcVariance();
		calcStd_deviation();
	}
	
	//gets
	
	T getMax(){return maxData;}
	T getMin(){return minData;}
	T getAverage(){return average;}
	T getVariance(){return variance;}
	T getStdDeviation(){return std_deviation;}
	
	//直接计算得出结果并返回值的函数:
	T getRange(){return maxData - minData;}	//计算极差 - 使用了最大值, 最小值
	T getMovingRange(){						//计算移动极差, 即最新两个数据的差(返回正数为增, 负数为减)
		//last data:  pos - 1
		//this data: pos - 2
		num_t thisDataIndex = pos == 0 ? dataNum - 1 : pos - 1;
		T& lastData  = data[thisDataIndex == 0 ? dataNum - 1 : thisDataIndex - 1];
		T& thisData = data[thisDataIndex];
		return thisData - lastData;
	}	
	// T getAvg_new_sub
	
	
	
	//operator overloading
	const T& operator[](num_t index){	//get data(not allow change)
		return data[index];
	}
	
	//添加数据, 并在每次添加时都重新计算各属性的值
	template <class URT>
	DataAnalysis& operator+(URT&& newData){
		push(newData);
		return *this;
	}
	//添加数据, 但不计算各属性的值
	template <class URT>
	DataAnalysis& operator<<(URT&& newData){
		push(newData, false);
		return *this;
	}
};
*/

// while(hasPain) singing(FOREVER) or sleeping(9_hour);

#endif
